---
title: 2. Writing your first schema
---

## Overview

In this chapter you're going to write your first schema. You'll learn about:

- Writing GraphQL objects
- Exposing GraphQL objects for query operations
- GraphQL SDL file generation
- Enhanced type safety & autocompletion

## Reflection?

Before we get going we need a moment to introduce an important part of the Nexus development workflow. Nexus has an unconventional concept called "Reflection". It refers to the fact that, when `makeSchema` is being called, not only is your application code being run, but **information is being gathered and artifacts are being derived**. Some of Nexus' uses for reflection include:

- Generating TypeScript types to give your resolvers complete type safety
- Generating an SDL file

This partly explains why Nexus has a declarative API. It needs a way to run your app reliably at build time. Declarative APIs give Nexus a higher degree of control to do this. Declarative APIs also encode enough semantic value for Nexus to do the things it needs to.

It also explains the need for the `--transpile-only` flag passed to `ts-node-dev`. If you don't know what it is about, **it basically tells TypeScript not to typecheck your app**. Since `nexus` needs to be run in order to generate TypeScript types, we need to ensure that `nexus` won't ever be prevented from generating these types because of a type error.

You might be (rightfully) wondering: "Wait, how am I supposed to benefit from Nexus' type-safety if I disable it in `ts-node-dev`... ?". For now, the answer to that is to use your IDE's type-checker. If your IDE/terminal doesn't have one, then manually run an additional `tsc` process.

Just remember the following though. You should _always_ have your `npm run dev` script running when you're working on your project _even_ when you're not intending to use your server (e.g. access the GraphQL Playground). If you forget to run `npm run dev` then you will not, for example, get the static typing experience that you expect in your resolvers.

## Model the domain

Let's get started with our blog schema by modeling some key entities in the domain. We'll begin with the concept of a `Post`. A post will represent a page in your blog. When not published, we'll call it a _draft_.

Your modeling work is going to start on the API layer as opposed to the database layer. This API-first approach can be a good way to collaborate with frontend teams, getting their input in shaping the data early.

> A note about terminology. We will be talking about the _Post Object_, not the _Post Model_. The difference is that at the API layer we have objects but at the database layer we have models. The name difference helps us talk about these different layers without confusion. It is also how GraphQL (API layer) and Prisma (database layer, discussed later) respectively refer to these things.

Create a new module for your Post object at `api/graphql/Post.ts`. We _could_ write our whole schema within say `api/schema.ts` or `api/graphql.ts`, but modularizing your GraphQL type definitions can help scale your codebase. Neither approach is inherently wrong though, so do as you see you fit. For this tutorial we'll use the modular style.

```bash-symbol copy
mkdir api/graphql && touch api/graphql/Post.ts
```

To create the `Post` object we'll import the `objectType` function from the `nexus` package. This will help you building a [GraphQL Object Types](https://graphql.org/graphql-js/object-types/).

<!-- prettier-ignore -->
```ts
// api/graphql/Post.ts

import { objectType } from 'nexus'

export const Post = objectType({
  name: 'Post',            // <- Name of your type
  definition(t) {
    t.int('id')            // <- Field named `id` of type `Int`
    t.string('title')      // <- Field named `title` of type `String`
    t.string('body')       // <- Field named `body` of type `String`
    t.boolean('published') // <- Field named `published` of type `Boolean`
  },
})
```

Additionally, we need to pass that `Post` object type down to our `makeSchema` function. To achieve that, we'll create an `api/graphql/index.ts` file, which will be used as an index to re-export all types from your schema.

```ts
// api/graphql/index.ts

export * from './Post'
```

Finally, we'll import this file and pass it down to `makeSchema`

<TabbedContent tabs={['Diff', 'Code']}>

<tab>

```ts
// api/schema.ts
import { makeSchema } from 'nexus'
import { join } from 'path'
+ import * as types from './graphql'

const schema = makeSchema({
-  types: []
+  types,
  outputs: {
    typegen: join(__dirname, '../nexus-typegen.ts'),
    schema: join(__dirname, '../schema.graphql')
  }
})
```

</tab>

<tab>

```ts
// api/schema.ts
import { makeSchema } from 'nexus'
import { join } from 'path'
import * as types from './graphql'

export const schema = makeSchema({
  types,
  outputs: {
    typegen: join(__dirname, '../nexus-typegen.ts'),
    schema: join(__dirname, '../schema.graphql'),
  },
})
```

</tab>

</TabbedContent>

> Note: It is considered best practice to pass your types directly from a "star import" like we've done above. Under the hood, Nexus will unwrap the types. This prevents from constantly having to manually export & import every single type of your API.

<div class="NextIs SectionDivider"></div>

## SDL?

Once you've saved this file change to disk, your app will be restarted.

You may notice that there's also now a new `schema.graphql` file at your project root. It contains a representation of your schema in a syntax called the GraphQL Schema Definition Language (SDL for short). In dev mode Nexus generates this for you at every app restart. In it you should see the following:

```graphql
type Post {
  body: String
  id: Int
  published: Boolean
  title: String
}
```

You are free to disable this file (settings discussed later) but its existence has two benefits for you to consider:

1. For users familiar with SDL the correspondence between the source code and it may help them learn Nexus' schema API faster.
2. The SDL syntax makes it an accessible way for others to evaluate incoming API changes without having to know about Nexus, or even JavaScript. Consider using the generated SDL file to improve your pull-request reviews.

For the remainder of this tutorial we'll be keeping SDL to the right of Nexus code blocks.

## Your first home-grown query

Your `Post` object is in place now but there's still no way for clients to read that data. Let's change that. You'll use the special `Query` object to expose your Post object.

We'll start by letting API clients read the drafts of your blog.

> NOTE: This should cause a static type error when you save the file. Don't worry, we'll fix it in a second!

<ParallelBlocks>

<block>

```ts
// api/graphql/Post.ts                   // 1

import { extendType } from 'nexus'

export const PostQuery = extendType({
  type: 'Query',                         // 2
  definition(t) {
    t.nonNull.list.field('drafts', {     // 3, 4, 5
      type: 'Post',                      // 6, 7
    })
  },
})
```

</block>

<block>

```graphql
type Query {
  drafts: [Post]!
}
```

</block>

</ParallelBlocks>

1. The Query object is a central place in your schema where many other types will appear. Like before with the modular GraphQL types decision we again can decide to be modular here. We could either create a new `api/graphql/Query.ts` module (not modular), or we could _collocate_ the exposure of Post object with its definition in `api/graphql/Post.ts` (modular). Staying consistent with before, we'll take the modular way.
1. To achieve collocation in Nexus we'll use `schema.extendType`. Its API is _very_ similar to `schema.objectType` with the difference that the defined fields are merged into the _targeted_ type.
1. `.nonNull` specifies that clients will always get a value for this field. By default, in Nexus, all "output types" (types returned by fields) are nullable. This is for [best practice reasons](https://graphql.org/learn/best-practices/#nullability). In this case though we indeed want to guarantee that a list will always be returned, never `null`.
   If you're ever dissatisfied with Nexus' defaults, not to worry, [you can change them](https://nexusjs.org/docs/api/make-schema#nonnulldefaults).
1. `.list` augments the field's type spec, making it wrapped by a List type. Here, a `[Post]`.
1. The first parameter specifies the field's name, here `drafts`
1. `type: 'Post'` specifies what the field's type should be. Here, a `Post`
1. Nexus also allows you to specifiy lists and nullability on the type field. This example could be rewritten like so 👇

  ```ts
  t.field('drafts', {
    type: nonNull(list('Post')),
  })
  ```



<div class="NextIs SectionDivider"></div>

<!-- ## Root Types -->

<!-- TODO rethink this content; diagrams; later; it implicates backing types... -->

<!-- There is one last thing to do here. You should be seeing error feedback from your IDE that the `resolve` field is missing. This is because `Query` (along with `Mutation` and `Subscription`) are _root types_. In GraphQL, the _fields_ of root types, unlike the fields of all other types, are _entrypoints_ into your API graph. And an entrypoint _must,_ intuitively, begin the process of getting data to fulfill the incoming operation.

Now, the `resolve` property is where you, the developer, implement this process of getting data. Put another way, the `resolve` property is where you implement the logic that fulfills the field's specification. You may be noting how when we defined our `Post` object, we did _not_ write resolvers for its fields. The reason for that is that Nexus provides _default_ resolvers for fields that are not root and that don't have resolvers. This default implementation is to return a property from the parent data of the same name as the field name. And when that's not possible (because the parent data diverges), then Nexus will let you know _statically_, requiring the resolver from you. What's awesome is that none of this is require knowledge to get productive with Nexus thanks to the static error that Nexus will give you along the way right in your IDE. Follow these and for the most part you'll fall into the pit of success. Awesome!

We will not go into more detail about the data resolution systems of GraphQL and Nexus just now. This was just a brief overview to give you a sense of what is going on. Mastering a complete mental model will take a bit of time and practice. -->

You'll see some feedback from your IDE that you're missing a `resolve` property. Go ahead and try to implement it, letting the autocompletion guide you.

> You might be wondering why Nexus hasn't complained about missing resolvers in some other cases so far. The answer is a more advanced topic that we'll cover later.

<TabbedContent tabs={['Diff', 'Code']}>

<tab>

```ts
// api/graphql/Post.ts
import { extendType } from 'nexus'
// ...

export const PostQuery = extendType({
  type: 'Query',
  definition(t) {
    t.nonNull.list.field('drafts', {
      type: 'Post',
+      resolve() {
+        return [{ id: 1, title: 'Nexus', body: '...', published: false }]
+      },
    })
  },
})
```

</tab>

<tab>

```ts
// api/graphql/Post.ts
import { extendType } from 'nexus'
// ...

export const PostQuery = extendType({
  type: 'Query',
  definition(t) {
    t.nonNull.list.field('drafts', {
      type: 'Post',
      resolve() {
        return [{ id: 1, title: 'Nexus', body: '...', published: false }]
      },
    })
  },
})
```

</tab>

</TabbedContent>

## Try it out

You can now open up your GraphQL playground and try the following query (left); In response, you should see something like so (right):

<ParallelBlocks>

<block>

```graphql copy
{
  drafts {
    id
    title
    body
    published
  }
}
```

</block>

<block>

```json
{
  "data": {
    "drafts": [
      {
        "id": 1,
        "title": "Nexus",
        "body": "...",
        "published": false
      }
    ]
  }
}
```

</block>

</ParallelBlocks>


## Wrapping up

Congratulations! You've successfully got your first GraphQL schema up and running with Nexus! In the next chapter we'll explore adding some write capabilities to our API.

<ButtonLink color="dark" type="primary" href="/getting-started/tutorial/chapter-adding-mutations-to-your-api">
  Next &rarr;
</ButtonLink>
